package com.pacvue.aspect;

import java.util.concurrent.TimeUnit;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import com.pacvue.anno.RedissonLock;
import com.pacvue.exception.RedissonLockException;
import com.pacvue.handler.LockFailureHandler;
import com.pacvue.properties.RedissonLockProperties;
import com.pacvue.utils.SpELUtils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Redisson分布式锁切面
 * 支持可重入锁、公平锁、读锁、写锁
 * 
 * @author pacvue
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class RedissonLockAspect {

    private final RedissonClient redissonClient;

    private final RedissonLockProperties redissonLockProperties;

    @Around("@annotation(redissonLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedissonLock redissonLock) throws Throwable {
        if (!redissonLockProperties.isEnabled()) {
            return joinPoint.proceed();
        }

        String lockKey = SpELUtils.parse(redissonLock.key(), joinPoint);
        RLock lock = getLock(redissonLock, lockKey);
        boolean locked = false;
        log.debug("线程{}尝试加锁{}", Thread.currentThread().getName(), lockKey);
        try {
            if (redissonLock.enableWatchdog() || redissonLock.leaseTime() <= 0) {
                // 启用看门狗（阻塞 + 自动续期）
                locked = lock.tryLock(redissonLock.waitTime(), TimeUnit.SECONDS);
            } else {
                // 不启用看门狗（阻塞 + 无续期 + 超时释放）
                locked = lock.tryLock(redissonLock.waitTime(), redissonLock.leaseTime(), TimeUnit.SECONDS);
            }
            
            if (!locked) {
                log.warn("线程{}获取分布式锁失败: {}", Thread.currentThread().getName(), lockKey);
                return handleLockFailure(redissonLock, joinPoint);
            }
            log.debug("线程{}成功获取锁: {}, 看门狗: {}, leaseTime: {}秒", 
                Thread.currentThread().getName(), lockKey, redissonLock.enableWatchdog(), redissonLock.leaseTime());
            return joinPoint.proceed();
        } finally {
            if (locked && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.debug("线程{}释放锁: {}", Thread.currentThread().getName(), lockKey);
            }
        }
    }

    /**
     * 根据锁类型获取对应的锁对象
     */
    private RLock getLock(RedissonLock redissonLock, String lockKey) {
        return switch (redissonLock.lockType()) {
            case FAIR -> redissonClient.getFairLock(lockKey);
            case READ -> redissonClient.getReadWriteLock(lockKey).readLock();
            case WRITE -> redissonClient.getReadWriteLock(lockKey).writeLock();
            default -> redissonClient.getLock(lockKey);
        };
    }

    /**
     * 处理获取锁失败的情况
     */
    private Object handleLockFailure(RedissonLock redissonLock, ProceedingJoinPoint joinPoint) {
        try {
            LockFailureHandler lockFailureHandler = redissonLock.failHandler().getDeclaredConstructor().newInstance();
            return lockFailureHandler.handle(joinPoint, redissonLock);
        } catch (Exception e) {
            log.error("获取锁失败处理类实例化失败: {}", e.getMessage());
            throw new RedissonLockException("获取锁失败处理类实例化失败");
        }
    }
}
